package it.giw.solr.Index;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;

public class IndexingTask implements Runnable {
	private BlockingQueue<String> queue;
	private CountDownLatch doneSignal;
	private static final String solrUrl = "http://localhost:8983/solr/update";
	private static HashMap<String, String> mimeMap;
	private String mode = "files";
	private static boolean mockMode = false;
	private int recursive = 0;
	private boolean auto = true;
	private String[] args;
	private String type = System.getProperty("type");
	private String fileTypes = "xml,json,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log";
	private int delay = 0;
	private OutputStream out;

	public IndexingTask(BlockingQueue<String> queue, CountDownLatch doneSignal) {
		this.queue = queue;
		this.doneSignal = doneSignal;
		mimeMap = new HashMap<String, String>();
		mimeMap.put("xml", "text/xml");
		mimeMap.put("csv", "text/csv");
		mimeMap.put("json", "application/json");
		mimeMap.put("pdf", "application/pdf");
		mimeMap.put("rtf", "text/rtf");
		mimeMap.put("html", "text/html");
		mimeMap.put("htm", "text/html");
		mimeMap.put("doc", "application/msword");
		mimeMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
		mimeMap.put("ppt", "application/vnd.ms-powerpoint");
		mimeMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
		mimeMap.put("xls", "application/vnd.ms-excel");
		mimeMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
		mimeMap.put("odt", "application/vnd.oasis.opendocument.text");
		mimeMap.put("ott", "application/vnd.oasis.opendocument.text");
		mimeMap.put("odp", "application/vnd.oasis.opendocument.presentation");
		mimeMap.put("otp", "application/vnd.oasis.opendocument.presentation");
		mimeMap.put("ods", "application/vnd.oasis.opendocument.spreadsheet");
		mimeMap.put("ots", "application/vnd.oasis.opendocument.spreadsheet");
		mimeMap.put("txt", "text/plain");
		mimeMap.put("log", "text/plain");
	}

	public void run() {
		while (!queue.isEmpty()) {
			String head = null;
			head = queue.poll();
			if (head == null)
				break;
			indexFile(head);
		}
		doneSignal.countDown();
	}

	private void indexFile(String head) {
		args = new String[1];
		args[0] = head;
		try {
			execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void execute() throws IOException {

		if (("files".equals(this.mode)) && (this.args.length > 0)) {
			doFilesMode();
		}
	}

	private void doFilesMode() {
		if (!this.args[0].equals("-")) {
			info("Posting files to base url "
					+ solrUrl
					+ (!this.auto ? " using content-type "
							+ (this.type == null ? "application/xml"
									: this.type) : "") + "..");
			if (this.auto) {
				info("Entering auto mode. File endings considered are "
						+ this.fileTypes);
			}
			if (this.recursive > 0) {
				info("Entering recursive mode, max depth=" + this.recursive
						+ ", delay=" + this.delay + "s");
			}
			postFile(new File(this.args[0]), this.out, this.type);
		}
	}

	static void info(String msg) {
		System.out.println(msg);
	}

	public static String appendParam(String url, String param) {
		String[] pa = param.split("&");
		for (String p : pa) {
			if (p.trim().length() != 0) {
				String[] kv = p.split("=");
				if (kv.length == 2) {
					url = url + (url.indexOf('?') > 0 ? "&" : "?") + kv[0]
							+ "=" + kv[1];
				} else {
					warn("Skipping param " + p
							+ " which is not on form key=value");
				}
			}
		}
		return url;
	}

	static void warn(String msg) {
		System.err.println("SimplePostTool: WARNING: " + msg);
	}

	public static void doGet(String url) {
		try {
			doGet(new URL(url));
		} catch (MalformedURLException e) {
			warn("The specified URL " + url
					+ " is not a valid URL. Please check");
		}
	}

	public static void doGet(URL url) {
		try {
			if (mockMode) {
				return;
			}
			HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
			if (200 != urlc.getResponseCode()) {
				warn("Solr returned an error #" + urlc.getResponseCode() + " "
						+ urlc.getResponseMessage() + " for url " + url);
			}
		} catch (IOException e) {
			warn("An error occurred posting data to " + url
					+ ". Please check that Solr is running.");
		}
	}

	public void postFile(File file, OutputStream output, String type) {
		InputStream is = null;
		try {

			URL slrURL = new URL(solrUrl);
			URL url = slrURL;
			if (this.auto) {
				if (type == null) {
					type = guessType(file);
				}
				if (type != null) {
					if ((!type.equals("text/xml"))
							&& (!type.equals("text/csv"))
							&& (!type.equals("application/json"))) {
						String urlStr = appendUrlPath(slrURL, "/extract")
								.toString();
						if (urlStr.indexOf("resource.name") == -1) {
							urlStr = appendParam(
									urlStr,
									"resource.name="
											+ URLEncoder.encode(
													file.getAbsolutePath(),
													"UTF-8"));
						}
						if (urlStr.indexOf("literal.id") == -1) {
							urlStr = appendParam(
									urlStr,
									"literal.id="
											+ URLEncoder.encode(
													file.getAbsolutePath(),
													"UTF-8"));
						}
						url = new URL(urlStr);
					}
				} else {
					warn("Skipping " + file.getName()
							+ ". Unsupported file type for auto mode.");
				}
			} else if (type == null) {
				type = "application/xml";
			}
			info("POSTing file " + file.getName()
					+ (this.auto ? " (" + type + ")" : ""));
			is = new FileInputStream(file);
			postData(is, Integer.valueOf((int) file.length()), output, type,
					url);
			return;
		} catch (IOException e) {
			e.printStackTrace();
			warn("Can't open/read file: " + file);
		} finally {
			try {
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				fatal("IOException while closing file: " + e);
			}
		}
	}

	static void fatal(String msg) {
		System.err.println("SimplePostTool: FATAL: " + msg);
		System.exit(2);
	}

	public boolean postData(InputStream data, Integer length,
			OutputStream output, String type, URL url) throws IOException {
		if (mockMode) {
			return true;
		}
		boolean success = true;
		if (type == null) {
			type = "application/xml";
		}
		HttpURLConnection urlc = null;
		try {
			try {
				urlc = (HttpURLConnection) url.openConnection();
				try {
					urlc.setRequestMethod("POST");
				} catch (ProtocolException e) {
					fatal("Shouldn't happen: HttpURLConnection doesn't support POST??"
							+ e);
				}
				urlc.setDoOutput(true);
				urlc.setDoInput(true);
				urlc.setUseCaches(false);
				urlc.setAllowUserInteraction(false);
				urlc.setRequestProperty("Content-type", type);
				if (null != length) {
					urlc.setFixedLengthStreamingMode(length.intValue());
				}
			} catch (IOException e) {
				fatal("Connection error (is Solr running at " + solrUrl
						+ " ?): " + e);
				success = false;
			}
			OutputStream out = null;
			try {
				out = urlc.getOutputStream();
				pipe(data, out);
			} catch (IOException e) {
				fatal("IOException while posting data: " + e);
				success = false;
			} finally {
			}
			try {
				if (200 != urlc.getResponseCode()) {
					warn("Solr returned an error #" + urlc.getResponseCode()
							+ " " + urlc.getResponseMessage());

					success = false;
				}
				InputStream in = urlc.getInputStream();
				pipe(in, output);
			} catch (IOException e) {
				warn("IOException while reading response: " + e);
				success = false;
			} finally {
			}
			urlc.disconnect();
		} finally {
			if (urlc != null) {
				urlc.disconnect();
			}
		}
		return success;
	}

	private static void pipe(InputStream source, OutputStream dest)
			throws IOException {
		byte[] buf = new byte[1024];
		int read = 0;
		while ((read = source.read(buf)) >= 0) {
			if (null != dest) {
				dest.write(buf, 0, read);
			}
		}
		if (null != dest) {
			dest.flush();
		}
	}

	protected static URL appendUrlPath(URL url, String append)
			throws MalformedURLException {
		return new URL(url.getProtocol() + "://" + url.getAuthority()
				+ url.getPath() + append
				+ (url.getQuery() != null ? "?" + url.getQuery() : ""));
	}

	protected static String guessType(File file) {
		String name = file.getName();
		String suffix = name.substring(name.lastIndexOf(".") + 1);
		return (String) mimeMap.get(suffix.toLowerCase(Locale.ROOT));
	}
}